package com.gao.a_thread.g._1;

import java.util.Arrays;

/**
 * @author gao
 * @time 2022/06/30 14:49:36
 * 该例子演示了使用if判断线程是否应该阻塞的逻辑会有什么问题
 *
 * 碗架
 */
class Shelf {
    private String[] strs = "○○○○○○".split("");
    private int i;

    public synchronized void put() {
        if (isFull()) {
            try {
                /*
                    1. 郭靖率先抢占到cpu，然后连续放了6个碗，郭靖睡在这里
                    2. 黄蓉随后抢占到cpu，取走一个碗，碗架此时有一个空槽（i==5），黄蓉调用notify随机唤醒一个线程，假设唤醒了郭靖
                       注意，郭靖虽然被唤醒了，但并没有竞争到锁
                    3. 杨过随后抢占到cpu，把碗架中唯一的空槽给添上了，杨过再想放碗时，发现碗架已满，杨过睡了
                    4. 郭靖此时抢占到cpu，郭靖会直接执行wait()后面的代码，此时碗架已满，郭靖放碗时造成数组下标越界，郭靖卒。
                 */
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        strs[i++] = "●";
        System.out.println(Thread.currentThread().getName() + "放入:" + this);
        if (i == 1) {
            notify();
        }
    }

    public synchronized void get() {
        if (isEmpty()) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        strs[--i] = "○";
        System.out.println(Thread.currentThread().getName() + "取出:" + this);
        if (i == 5) {
            notify();
        }
    }

    public boolean isFull() {
        return i == strs.length;
    }

    public boolean isEmpty() {
        return i == 0;
    }

    @Override
    public String toString() {
        return Arrays.toString(strs);
    }
}

/**
 * 放碗任务
 */
class T implements Runnable {
    private Shelf sh;

    public T(Shelf sh) {
        this.sh = sh;
    }

    @Override
    public void run() {
        while (true) {
            sh.put();
        }
    }
}

/**
 * 取碗任务
 */
class S implements Runnable {
    private Shelf sh;

    public S(Shelf sh) {
        this.sh = sh;
    }

    @Override
    public void run() {
        while (true) {
            sh.get();
        }
    }
}

public class App {
    public static void main(String[] args) {
        Shelf sh = new Shelf();
        T t = new T(sh);
        S s = new S(sh);
        Thread th = new Thread(t, "郭靖");
        Thread th2 = new Thread(t, "杨过");
        Thread th3 = new Thread(s, "黄蓉");
        Thread th4 = new Thread(s, "龙女");

        th.start();
        th2.start();
        th3.start();
        th4.start();
    }
}